// -----------------------------------------------------------------------
//  <copyright file="SendValidateCodeCommand.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/7 19:35:14</last-date>
// -----------------------------------------------------------------------

using DaprPlus.Extensions;
using DaprPlus.Identity.Domain.Entities;
using DaprPlus.Identity.Domain.Services;
using DaprPlus.Identity.Domain.Specs;
using DaprPlus.Net;
using DaprPlus.Security;
using DaprPlus.Specifications;

using Microsoft.Extensions.DependencyInjection;


namespace DaprPlus.Identity.Application.Identity.Users.Commands
{
    public class SendValidateCodeCommand : IRequest<ApiResult>, ICommand
    {
        public string Account { get; set; } = null!;

        public ValidateCodeType Type { get; set; }

        public string CaptchaId { get; set; } = null!;

        public string Captcha { get; set; } = null!;
    }

    public class SendEmailValidateCodeCommandHandler(IServiceProvider provider) : IRequestHandler<SendValidateCodeCommand, ApiResult>
    {
        public async Task<ApiResult> Handle(SendValidateCodeCommand request, CancellationToken token)
        {
            var siteSetting = await provider.GetSettingAsync<SiteSetting>(token);
            var validateCodeSetting = await provider.GetSettingAsync<ValidateCodeSetting>(token);
            var validateCodeService = provider.GetRequiredService<IValidateCodeService>();

            var isEmail = request.Account.IsEmail();
            var seconds = isEmail ? validateCodeSetting.EmailCodeSeconds.Value : validateCodeSetting.SmsCodeSeconds.Value;
            var code = await validateCodeService.GenerateAsync(request.Account, request.Type, seconds, token);

            var message = isEmail
                ? GetEmailMessage(siteSetting, validateCodeSetting, request.Account, code.Code, seconds)
                : GetSmsMessage(siteSetting, validateCodeSetting, code.Code, seconds);

            await SendMessageAsync(isEmail, request.Account, siteSetting, message);

            var notifyType = request.Type.ToDescription() + (isEmail ? " 邮件" : " 短信");
            return new ApiResult($"{notifyType}验证码发送成功");
        }

        private async Task SendMessageAsync(bool isEmail, string account, SiteSetting siteSetting, string message)
        {
            if (isEmail)
            {
                var emailSender = provider.GetRequiredService<IEmailSender>();
                await emailSender.SendAsync(account, $"【{siteSetting.ShortSiteName.Value}】验证码邮件", message);
            }
            else
            {
                var smsSender = provider.GetRequiredService<ISmsSender>();
                await smsSender.SendAsync(account, message);
            }
        }

        private static string GetEmailMessage(SiteSetting siteSetting, ValidateCodeSetting validateCodeSetting, string email, string code, int seconds)
        {
            return validateCodeSetting.EmailCodeMessageFormat.Value!
                .Replace("{shortSiteName}", siteSetting.ShortSiteName.Value)
                .Replace("{email}", email)
                .Replace("{code}", code)
                .Replace("{minutes}", (seconds / 60).ToString())
                .Replace("{companyName}", siteSetting.CompanyName.Value);
        }

        private static string GetSmsMessage(SiteSetting siteSetting, ValidateCodeSetting validateCodeSetting, string code, int seconds)
        {
            return validateCodeSetting.SmsCodeMessageFormat.Value!
                .Replace("{shortSiteName}", siteSetting.ShortSiteName.Value)
                .Replace("{code}", code)
                .Replace("{minutes}", (seconds / 60).ToString());
        }
    }


    public class SendValidateCodeCommandValidator : AbstractValidator<SendValidateCodeCommand>
    {
        public SendValidateCodeCommandValidator(IServiceProvider provider)
        {
            ClassLevelCascadeMode = CascadeMode.Stop;
            RuleFor(x => x.CaptchaId).NotEmpty();
            RuleFor(x => x.Captcha).NotEmpty().Must((request, value) =>
            {
                var captchaService = provider.GetRequiredService<ICaptchaService>();
                var flag = captchaService.Validate(request.CaptchaId, value, true);
                return flag;
            }).WithMessage("验证码不正确");

            RuleFor(x => x.Account).NotEmpty()
                .Must(val => val.IsEmail() || val.IsMobileNumber()).WithMessage("发送验证码的账号必须是邮箱或手机号");

            ValidateCodeType[] needAccountValidTypes = [
                ValidateCodeType.Login,
                ValidateCodeType.ResetPassword,
                ValidateCodeType.ChangePassword
            ];
            When(x => needAccountValidTypes.Contains(x.Type), () =>
            {
                RuleFor(x => x.Account).MustAsync(async (request, account, token) =>
                {
                    ISpecification<User> spec = account.IsEmail()
                        ? new UserByNormalizedEmailSpec<User>(account.ToUpper())
                        : new UserByPhoneNumberSpec<User>(account);
                    var userRepository = provider.GetRepository<User>();
                    return await userRepository.AnyAsync(spec, token);
                }).WithMessage(x => $"发送验证码时账号 {x.Account} 不存在");
            });

        }
    }
}
